﻿using System;
using System.Collections;
using System.Windows.Forms;

namespace SupervisedSpeakerRecognition
{
    /// <summary>
    /// Class for operating all MFCC process
    /// </summary>
    public class CepstralAnalysisManager
    {
        //************************  Private Fields  *******************************// 
        private FeatureExtractorData[] saveData;
        private String[] inputFiles;
        private String[] inputFilesNamesOnly;
        private double alpha;
        private int winSize;
        private bool usingHamming;
        private bool useStripSilence;
        private bool realOnly;
        private bool useFirstCoeffAC;
        private bool getHighFreq;
        private int overLap;
        private bool powMagnitude;
        private int numberOfMFCC;
        private bool usingMelFilter;
        private int numberOfClusters;
        private int numberOfSteps = 6;
        private String windowType;     // Hamming window
        private DistanceMeasurement distanceType;
        private CommandInvoker invoker;
        private WavFileExtractorCommand wavReadCmd;
        private PreEmphasisCommand preEmphasisCommand;
        private CreateFramesCommand createFramesCommand;
        private FFTCommand fftcommand;
        private LogEnergyMagnitudeCommand logMagCommand;
        private IFFTCommand ifftCommand;
        private SilenceRedeuceCommand silenceRedeuceCommand;

        //************************  Constructors    *******************************// 
        public CepstralAnalysisManager()
        {
            invoker = new CommandInvoker();
            wavReadCmd = new WavFileExtractorCommand();
            preEmphasisCommand = new PreEmphasisCommand();
            createFramesCommand = new CreateFramesCommand();
            fftcommand = new FFTCommand();
            logMagCommand = new LogEnergyMagnitudeCommand();
            ifftCommand = new IFFTCommand();
            silenceRedeuceCommand = new SilenceRedeuceCommand();
        }


        #region public methods
        //************************  Public Methods  ****************************// 
        /// <summary>
        /// 
        /// </summary>
        public void clearLogicManagerResults()
        {
            invoker = new CommandInvoker();
            wavReadCmd = new WavFileExtractorCommand();
            preEmphasisCommand = new PreEmphasisCommand();
            createFramesCommand = new CreateFramesCommand();
            fftcommand = new FFTCommand();
            logMagCommand = new LogEnergyMagnitudeCommand();
            ifftCommand = new IFFTCommand();
        }

        /// <summary>
        /// This method will run the full process 
        /// </summary>
        /// <param name="progressBar_Main">Progress bar GUI element</param>
        /// <param name="labelProg">Label for process steps</param>
        /// <returns></returns>
        public void runCompleteProcess(ToolStripProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            // create a feature data saver for each input WAV file 
            saveData = new FeatureExtractorData[inputFiles.Length];
            // calculate the maximum value of the progress bar
            progressBar_Main.Maximum = inputFiles.Length * numberOfSteps * progressBar_Main.Step;

            // create command invoker for invoking the commands


            // loop to extract features from each input file
            for (int i = 0; i < inputFiles.Length; i++)
            {
                // create a new save object
                saveData[i] = new FeatureExtractorData(inputFiles[i]);
                wavFileExtractorPerformed(i, progressBar_Main, labelProg);
                if (useStripSilence)
                {
                    silenceRedeucePreform(i, labelProg);
                }
                preEmphasisPerform(i, progressBar_Main, labelProg);
                createFramesPerformed(i, progressBar_Main, labelProg);
                fftPerformed(i, progressBar_Main, labelProg);
                logEnergyMagnitudePerformed(i, progressBar_Main, labelProg);
                iffPerformed(i, progressBar_Main, labelProg);
            }
        }
        #endregion

        #region ICommand Preform
        /// <summary>
        ///  
        /// </summary>
        /// <param name="i"></param>
        /// <param name="labelProg"></param>
        private void silenceRedeucePreform(int i, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing silence reduction...");
            silenceRedeuceCommand.InputData = saveData[i].WavData;
            silenceRedeuceCommand.FrameSize = WinSize;
            invoker.Invoke(silenceRedeuceCommand);
            saveData[i].WavData = silenceRedeuceCommand.OutputData;

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void preEmphasisPerform(int i, ToolStripProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing PreEmphasis...");
            preEmphasisCommand.Alpha = alpha;
            preEmphasisCommand.InputData = saveData[i].WavData;
            invoker.Invoke(preEmphasisCommand);
            saveData[i].PreEmpData = preEmphasisCommand.OutputData;
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void wavFileExtractorPerformed(int i, ToolStripProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": reading WAV data...");
            wavReadCmd.FilePath = saveData[i].WavFileName;
            wavReadCmd.UsingStereo = true;
            invoker.Invoke(wavReadCmd);
            saveData[i].WavData = wavReadCmd.OutPutWithOutHeader;
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void createFramesPerformed(int i, ToolStripProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing Windowing...");
            createFramesCommand.InputData = saveData[i].PreEmpData;
            createFramesCommand.UsingHammingWindow = usingHamming;
            createFramesCommand.WindowSize = winSize;
            createFramesCommand.OverlapSize = overLap;
            createFramesCommand.WindowType = windowType;
            invoker.Invoke(createFramesCommand);
            saveData[i].FramingData = createFramesCommand.OutputData;
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void fftPerformed(int i, ToolStripProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing FFT on frames...");
            // execute FFT on frames of the input data
            fftcommand.RealOnly = realOnly;
            double[][] twoDimArr = saveData[i].FramingData;
            saveData[i].FftData = new double[twoDimArr.Length][];
            for (int j = 0; j < twoDimArr.Length; j++)
            {
                fftcommand.InputData = twoDimArr[j];
                invoker.Invoke(fftcommand);
                saveData[i].FftData[j] = fftcommand.MagnitudeData;
            }
            saveData[i].FftDataOneDim = convert2DimArrayTo1Dim(saveData[i].FftData);
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void logEnergyMagnitudePerformed(int i, ToolStripProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing Log energy...");
            // execute Log Energy and framing on the input data
            double[][] logArr = saveData[i].FftData;
            saveData[i].LogEnergyData = new double[logArr.Length][];
            logMagCommand.WithPowerOfTwo = powMagnitude;
            for (int j = 0; j < logArr.Length; j++)
            {
                logMagCommand.InputData = logArr[j];
                invoker.Invoke(logMagCommand);
                saveData[i].LogEnergyData[j] = logMagCommand.OutPutData;
            }
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void iffPerformed(int i, ToolStripProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing IFFT on frames...");
            // execute IFFT on frames of the input data
            double[][] ifftArr = saveData[i].LogEnergyData;
            saveData[i].IfftData = new double[ifftArr.Length][];
            for (int j = 0; j < ifftArr.Length; j++)
            {
                ifftCommand.InputData = ifftArr[j];
                invoker.Invoke(ifftCommand);
                saveData[i].IfftData[j] = ifftCommand.MagnitudeData;
            }
            saveData[i].IfftDataOneDim = convert2DimArrayTo1Dim(saveData[i].IfftData);
            performOneStep(progressBar_Main);
        }
        #endregion

        #region Getters and Setters


        public String[] InputFilesNamesOnly
        {
            get { return inputFilesNamesOnly; }
            set { inputFilesNamesOnly = value; }
        }

        public int WinSize
        {
            get { return winSize; }
            set { winSize = value; }
        }

        public String WindowType
        {
            get { return windowType; }
            set { windowType = value; }
        }

        public int OverLap
        {
            get { return overLap; }
            set { overLap = value; }
        }

        public bool UsingHamming
        {
            get { return usingHamming; }
            set { usingHamming = value; }
        }

        public bool UseStripSilence
        {
            get { return useStripSilence; }
            set { useStripSilence = value; }
        }

        public double Alpha
        {
            get { return alpha; }
            set { alpha = value; }
        }

        public String[] InputFiles
        {
            get { return inputFiles; }
            set { inputFiles = value; }
        }

        public FeatureExtractorData[] SaveData
        {
            get { return saveData; }
            set { saveData = value; }
        }

        public bool RealOnly
        {
            get { return realOnly; }
            set { realOnly = value; }
        }

        public bool PowMagnitude
        {
            get { return powMagnitude; }
            set { powMagnitude = value; }
        }

        public int NumberOfMFCC
        {
            get { return numberOfMFCC; }
            set { numberOfMFCC = value; }
        }

        public bool GetHighFreq
        {
            get { return getHighFreq; }
            set { getHighFreq = value; }
        }

        public bool UseFirstCoeffAC
        {
            get { return useFirstCoeffAC; }
            set { useFirstCoeffAC = value; }
        }

        public int NumberOfClusters
        {
            get { return numberOfClusters; }
            set { numberOfClusters = value; }
        }

        public DistanceMeasurement DistanceType
        {
            get { return distanceType; }
            set { distanceType = value; }
        }

        public bool UsingMelFilter
        {
            get { return usingMelFilter; }
            set { usingMelFilter = value; }
        }

        public int NumberOfSteps
        {
            get { return numberOfSteps; }
            set { numberOfSteps = value; }
        }

        #endregion

        #region private methods
        //************************  Private Methods  ****************************// 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        private double findMaxValueInArray(double[] array)
        {
            double max = array[0];
            for (int i = 1; i < array.Length; i++)
            {
                if (array[i] > max)
                    max = array[i];
            }
            return max;
        }


        /// <summary>
        /// perform a step in the GUI progress bar
        /// </summary>
        private void performOneStep(ToolStripProgressBar progressBar_Main)
        {
            progressBar_Main.PerformStep();
            //progressBar_Main.Update();
        }

        /// <summary>
        /// perform a step in the GUI label
        /// </summary>
        /// <param name="labelProg"></param>
        /// <param name="text"></param>
        private void updateProgressLabel(System.Windows.Forms.Label labelProg, String text)
        {
            labelProg.Text = text;
            //labelProg.Visible = false;
            //labelProg.Visible = true;
            labelProg.Update();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private double[] convert2DimArrayTo1Dim(double[][] source)
        {
            int rows = source.Length, cols = source[0].Length;
            ArrayList tempArr = new ArrayList();
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    tempArr.Add(source[i][j]);
                }
            }
            double[] temp = new double[tempArr.Count];

            for (int i = 0; i < tempArr.Count; i++)
            {
                temp[i] = (double)tempArr[i];
            }
            return temp;
        }
    }
        #endregion
}
